home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Special 17 / AMIGAplus Sonderheft 17 (1999)(ICP)(DE)[!].iso / PD / Anwendungen / -DataTypes- / mpegaudio / dispatch.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  20KB  |  619 lines

  1.  
  2. /*
  3. **
  4. **  $VER: dispatch.c 2.2 (1.5.98)
  5. **  mpegaudio.datatype 2.2
  6. **
  7. **  Dispatch routine for a DataTypes class
  8. **
  9. **  Written 1996-1998 by Roland 'Gizzy' Mainz
  10. **  Original example source from David N. Junod
  11. **
  12. */
  13.  
  14. /* main includes */
  15. #include "classbase.h"
  16. #include "classdata.h"
  17.  
  18. /*****************************************************************************/
  19.  
  20. /* local prototypes */
  21. static DISPATCHERFLAGS ULONG            Dispatch( REGA0 struct IClass *, REGA2 Object *, REGA1 Msg );
  22. static                 BOOL             LoadSample( struct ClassBase *, Object * );
  23.  
  24. #ifndef NO_ENCODER
  25. static                 ULONG            SaveMPEGAudio( struct ClassBase *, Object *, struct dtWrite * );
  26. #endif /* NO_ENCODER */
  27.  
  28. static DISPATCHERFLAGS ULONG            bitstream_hook( REGA0 struct Hook *, REGA2 APTR , REGA1 MPEGA_ACCESS * );
  29.  
  30.  
  31. /*****************************************************************************/
  32.  
  33. /* Create "mpegaudio.datatype" BOOPSI class */
  34. struct IClass *initClass( struct ClassBase *cb )
  35. {
  36.     struct IClass *cl;
  37.  
  38.     /* Create our class... */
  39.     if( cl = MakeClass( MPEGAUDIODTCLASS, SOUNDDTCLASS, NULL, (ULONG)sizeof( struct MPEGAudioInstData ), 0UL ) )
  40.     {
  41. #define DTSTACKSIZE (16384UL)
  42.       cl -> cl_Dispatcher . h_Entry    = (HOOKFUNC)StackSwapDispatch; /* see stackswap.c */
  43.       cl -> cl_Dispatcher . h_SubEntry = (HOOKFUNC)Dispatch;          /* see stackswap.c */
  44.       cl -> cl_Dispatcher . h_Data     = (APTR)DTSTACKSIZE;           /* see stackswap.c */
  45.       cl -> cl_UserData                = (ULONG)cb;                   /* class library base as expected by datatypes.library */
  46.  
  47.       AddClass( cl );
  48.     }
  49.  
  50.     return( cl );
  51. }
  52.  
  53. /*****************************************************************************/
  54.  
  55. /* class dispatcher */
  56. static
  57. DISPATCHERFLAGS
  58. ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
  59. {
  60.     struct ClassBase     *cb = (struct ClassBase *)(cl -> cl_UserData);
  61.     ULONG                 retval = 0UL;
  62.  
  63.     switch( msg -> MethodID )
  64.     {
  65. /****** mpegaudio.datatype/OM_NEW ********************************************
  66. *
  67. *    NAME
  68. *        OM_NEW -- Create a mpegaudio.datatype object.
  69. *
  70. *    FUNCTION
  71. *        The OM_NEW method is used to create an instance of the
  72. *        mpegaudio.datatype class. This method is passed to the superclass
  73. *        first. After this, mpegaudio.datatype loads the sample using
  74. *        "mpega.library"'s decoder into memory.
  75. *
  76. *    ATTRIBUTES
  77. *        The following attributes can be specified at creation time.
  78. *
  79. *        DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
  80. *            attribute. Only DTST_FILE is supported.
  81. *            If any other type was set in a given DTA_SourceType,
  82. *            OM_NEW will be rejected.
  83. *            Defaults to DTST_FILE.
  84. *
  85. *        DTA_Handle -- For DTST_FILE, a BPTR to a lock is expected by
  86. *            datatypesclass, which will convert this into a filehandle.
  87. *            A DTST_RAM (create empty object) source type requires a NULL
  88. *            handle.
  89. *
  90. *    NOTE
  91. *        If the datatype was compiled with the NO_ENCODER flag set,
  92. *        DTA_SourceType == DTST_RAM causes OM_NEW to reject the method.
  93. *
  94. *    RESULT
  95. *        If the object was created a pointer to the object is returned,
  96. *        otherwise NULL is returned.
  97. *
  98. ******************************************************************************
  99. *
  100. */
  101.       case OM_NEW:
  102.       {
  103.           struct TagItem *ti;
  104.  
  105.           /* We only support DTST_FILE or DTST_RAM as source type */
  106.           if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
  107.           {
  108.             if( ((ti -> ti_Data) != DTST_FILE)
  109. #ifndef NO_ENCODER
  110. && ((ti -> ti_Data) != DTST_RAM)
  111. #endif /* !NO_ENCODER */
  112. )
  113.             {
  114.               SetIoErr( ERROR_OBJECT_WRONG_TYPE );
  115.  
  116.               break;
  117.             }
  118.           }
  119.  
  120.           /* This must not be a subclass of mpegaudio.datatype
  121.            * (not implemented yet)
  122.            */
  123.           if( o == (Object *)cl )
  124.           {
  125.             if( retval = DoSuperMethodA( cl, o, msg ) )
  126.             {
  127.               /* Load sample... */
  128.               if( !LoadSample( cb, (Object *)retval ) )
  129.               {
  130.                 /* Something went fatally wrong, dispose object */
  131.                 CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
  132.                 retval = 0UL;
  133.               }
  134.             }
  135.           }
  136.           else
  137.           {
  138.             /* Subclasses of mpegaudio.datatype are not implemented */
  139.             SetIoErr( ERROR_NOT_IMPLEMENTED );
  140.           }
  141.       }
  142.           break;
  143.  
  144. /****** mpegaudio.datatype/OM_DISPOSE ****************************************
  145. *
  146. *    NAME
  147. *        OM_DISPOSE -- Delete object
  148. *
  149. *    FUNCTION
  150. *        Frees the contents of the mpegvideo.datatype instance data
  151. *        and passes then the msg to the superclass.
  152. *
  153. *    RESULT
  154. *        Returns 0 evertimes.
  155. *
  156. ******************************************************************************
  157. *
  158. */
  159.       case OM_DISPOSE:
  160.       {
  161.           LONG saved_ioerr = IoErr(); /* save I/O error */
  162.  
  163.           /* Free ourselves */
  164.           DoSuperMethodA( cl, o, msg );
  165.  
  166.           /* Restore I/O error */
  167.           SetIoErr( saved_ioerr );
  168.       }
  169.           break;
  170.  
  171.       case OM_UPDATE:
  172.       {
  173.           if( DoMethod( o, ICM_CHECKLOOP ) )
  174.           {
  175.             break;
  176.           }
  177.       }
  178.       case OM_SET:
  179.       {
  180.           /* Pass the attributes to the sound class and force a refresh if we need it */
  181.           if( retval = DoSuperMethodA( cl, o, msg ) )
  182.           {
  183. /* The following check statement isn't needed because OM_NEW does not allow subclasses of mpegaudio.datatype,
  184.  * therefore, we're always the top instance...
  185.  */
  186. #ifdef COMMENTED_OUT
  187.             /* Top instance ? */
  188.             if( OCLASS( o ) == cl )
  189. #endif /* COMMENTED_OUT */
  190.             {
  191.               struct RastPort *rp;
  192.  
  193.               /* Get a pointer to the rastport */
  194.               if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
  195.               {
  196.                 struct gpRender gpr;
  197.  
  198.                 /* Force a redraw */
  199.                 gpr . MethodID   = GM_RENDER;
  200.                 gpr . gpr_GInfo  = ((struct opSet *)msg) -> ops_GInfo;
  201.                 gpr . gpr_RPort  = rp;
  202.                 gpr . gpr_Redraw = GREDRAW_UPDATE;
  203.  
  204.                 DoMethodA( o, (Msg)(&gpr) );
  205.  
  206.                 /* Release the temporary rastport */
  207.                 ReleaseGIRPort( rp );
  208.  
  209.                 /* We did a refresh... */
  210.                 retval = 0UL;
  211.               }
  212.             }
  213.           }
  214.       }
  215.           break;
  216.  
  217. /****** mpegaudio.datatype/DTM_WRITE **********************************************
  218. *
  219. *    NAME
  220. *        DTM_WRITE -- Save data
  221. *
  222. *    FUNCTION
  223. *        This method saves the object's contents to disk.
  224. *
  225. *        If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
  226. *        superclass, sound.datatype, which writes an IFF 8SVX sample.
  227. *
  228. *        If dtw_mode is DTWM_RAW, the object writes a MPEG audio stream to
  229. *        the filehandle given.
  230. *        (If the class library was compiled with the NO_ENCODER switch
  231. *        (not the default), result == 0 and resul2 == ERROR_NOT_IMPLEMENTED
  232. *        are returned).
  233. *
  234. *    TAGS
  235. *        None defined.
  236. *
  237. *    RESULT
  238. *        Returns 0 for failure (IoErr() returns result2), non-zero
  239. *        for success.
  240. *
  241. ******************************************************************************
  242. *
  243. */
  244.       case DTM_WRITE:
  245.       {
  246.           struct dtWrite *dtw;
  247.  
  248.           dtw = (struct dtWrite *)msg;
  249.  
  250.           /* Local data format requested ? */
  251.           if( (dtw -> dtw_Mode) == DTWM_RAW )
  252.           {
  253. /* Enable the followng code if you don't have an encoder implemented... */
  254. #ifdef NO_ENCODER
  255.             SetIoErr( ERROR_NOT_IMPLEMENTED );
  256.             retval = 0UL;
  257. #else
  258.             retval = SaveMPEGAudio( cb, o, dtw );
  259. #endif /* NO_ENCODER */
  260.           }
  261.           else
  262.           {
  263.             /* Pass msg to superclass (which writes an IFF 8SVX sample)... */
  264.             retval = DoSuperMethodA( cl, o, msg );
  265.           }
  266.       }
  267.           break;
  268.  
  269.       /* Let the superclass handle everything else */
  270.       default:
  271.       {
  272.           retval = DoSuperMethodA( cl, o, msg );
  273.       }
  274.           break;
  275.     }
  276.  
  277.     return( retval );
  278. }
  279.  
  280.  
  281. static
  282. BOOL LoadSample( struct ClassBase *cb, Object *o )
  283. {
  284.     struct MPEGAudioInstData *maid    = (struct MPEGAudioInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
  285.     LONG                      error   = 0L;
  286.     BOOL                      success = FALSE;
  287.     BPTR                      fh;               /* stream handle                                */
  288.     ULONG                     sourcetype;       /* type of stream (either DTST_FILE or DTST_RAM */
  289.     struct VoiceHeader       *vh;               /* obj's voice header                           */
  290.  
  291.     /* Get file handle, handle type and VoiceHeader */
  292.     if( GetDTAttrs( o, DTA_SourceType,    (&sourcetype),
  293.                        DTA_Handle,        (&fh),
  294.                        SDTA_VoiceHeader,  (&vh),
  295.                        TAG_DONE ) == 3UL )
  296.     {
  297.       switch( sourcetype )
  298.       {
  299.         case DTST_FILE:
  300.         {
  301.             /* We got all what we want (a filehandle) */
  302.         }
  303.             break;
  304.  
  305. #ifndef NO_ENCODER
  306.         case DTST_RAM:
  307.         {
  308.             /* Do nothing... */
  309.         }
  310.             break;
  311. #endif /* !NO_ENCODER */
  312.  
  313.         default:
  314.         {
  315.             /* unsupported source type */
  316.             error = ERROR_NOT_IMPLEMENTED;
  317.         }
  318.             break;
  319.       }
  320.  
  321.       /* Any error ? */
  322.       if( error == 0L )
  323.       {
  324.         if( fh )
  325.         {
  326.           BYTE         *sample     = NULL;
  327.           LONG          pcm_pos    = 0L;   /* position in buffer */
  328.           struct Hook   accesshook = { 0 };
  329.  
  330.           MPEGA_CTRL mpa_ctrl =
  331.           {
  332.              NULL,          // Bitstream access hook, see below (set to (&accesshook))
  333.              // Layers I & II settings (mono, stereo)
  334.              { FALSE, { 2, 0, 20000 }, { 2, 0, 20000 } },
  335.              // Layer III settings (mono, stereo)
  336.              { FALSE, { 2, 0, 20000 }, { 2, 0, 20000 } },
  337.              0,             // Don't check mpeg validity at start (needed for mux stream)
  338.              1024           // Stream Buffer size
  339.           };
  340.  
  341.           ULONG samplesize = 0UL;
  342.  
  343.           /* init hook */
  344.           accesshook . h_Entry    = (HOOKFUNC)bitstream_hook;
  345.           accesshook . h_SubEntry = (HOOKFUNC)cb; /* misused here as 2nd data field... */
  346.           accesshook . h_Data     = (APTR)fh;
  347.           mpa_ctrl . bs_access    = (&accesshook);
  348.  
  349.           if( maid -> maid_MPAS = MPEGA_open( NULL, (&mpa_ctrl) ) )
  350.           {
  351.             WORD  left[ MPEGA_PCM_SIZE + 32 ],
  352.                   right[ MPEGA_PCM_SIZE + 32 ],
  353.                  *pcm[ 2 ];
  354.             LONG  pcm_count;
  355.  
  356.             pcm[ 0 ] = left;
  357.             pcm[ 1 ] = right;
  358.  
  359.             /* Decoder frames into a continous ram buffer; if the buffer gets to small, enlarge it... */
  360.             while( (pcm_count = MPEGA_decode_frame( (maid -> maid_MPAS), pcm )) >= 0 )
  361.             {
  362.               BYTE  *newsample;
  363.               ULONG  next  = pcm_pos + pcm_count + 32;
  364.  
  365.               if( samplesize <= next )
  366.               {
  367.                 next += MPEGA_PCM_SIZE * 256UL;
  368.  
  369.                 if( newsample = (BYTE *)AllocVec( (next + 256), MEMF_PUBLIC ) )
  370.                 {
  371.                   /* "sample" and "newsample" are long-word aligned, and "pcm_pos" can be padded without trouble,
  372.                    * therefore I can use CopyMemQuick here...
  373.                    */
  374.                   CopyMemQuick( sample, newsample, ((pcm_pos + 3UL) & ~3UL) );
  375.                   FreeVec( sample );
  376.                   sample     = newsample;
  377.                   samplesize = next;
  378.                 }
  379.                 else
  380.                 {
  381.                   /* no buffer memory */
  382.                   pcm_count = MPEGA_ERR_MEM;
  383.                   break;
  384.                 }
  385.               }
  386.  
  387. #ifdef COMNTED_OUT
  388.               /* KISS PCM->sample conversion function */
  389.               {
  390.                 LONG   i;
  391.  
  392.                 /* Stereo ? */
  393.                 if( (maid -> maid_MPAS -> channels) == 2 )
  394.                 {
  395.                   /* Convert left+right channels */
  396.                   for( i = 0L ; i < pcm_count ; i++ )
  397.                   {
  398.                     sample[ i + pcm_pos ] = ((left[ i ] + right[ i ]) >> 9); /* ((left + right) / 2) >> 8 */
  399.                   }
  400.                 }
  401.                 else
  402.                 {
  403.                   /* Convert left channel (mono) only */
  404.                   for( i = 0L ; i < pcm_count ; i++ )
  405.                   {
  406.                     sample[ i + pcm_pos ] = (left[ i ] >> 8); /* ((left + right) / 2) >> 8 */
  407.                   }
  408.                 }
  409.               }
  410. #else
  411.               /* This replacement for the loop above is much faster because
  412.                * the ratio "loop-check vs. PCM->sample-conversion" is ~~ 1:8
  413.                * instead of 1:1 (like above)...
  414.                */
  415.               {
  416.                 /* Stereo ? */
  417.                 if( (maid -> maid_MPAS -> channels) == 2 )
  418.                 {
  419.                   /* Convert left+right channels */
  420.                   register ULONG blocks = (pcm_count & ~(0x07UL)) >> 3UL,
  421.                                  cut    = pcm_count &  (0x07UL),
  422.                                  i;
  423.                   register BYTE *sptr   = sample + pcm_pos;
  424.                   register WORD *lbptr  = left,
  425.                                 *rbptr  = right;
  426.  
  427.                   /* Copy sample blocks... */
  428.                   for( i = 0UL ; i < blocks ; i++ )
  429.                   {
  430.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  431.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  432.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  433.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  434.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  435.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  436.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  437.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  438.                   }
  439.  
  440.                   /* ... then the remaining samples. */
  441.                   for( i = 0UL ; i < cut ; i++ )
  442.                   {
  443.                     *sptr++ = ((*lbptr++ + *rbptr++) >> 9);
  444.                   }
  445.                 }
  446.                 else
  447.                 {
  448.                   /* Convert left channel (mono) only */
  449.                   register ULONG blocks = (pcm_count & ~(0x07UL)) >> 3UL,
  450.                                  cut    = pcm_count &  (0x07UL),
  451.                                  i;
  452.                   register BYTE *sptr   = sample + pcm_pos;
  453.                   register WORD *lbptr  = left;
  454.  
  455.                   /* Copy sample blocks... */
  456.                   for( i = 0UL ; i < blocks ; i++ )
  457.                   {
  458.                     *sptr++ = (*lbptr++ >> 8);
  459.                     *sptr++ = (*lbptr++ >> 8);
  460.                     *sptr++ = (*lbptr++ >> 8);
  461.                     *sptr++ = (*lbptr++ >> 8);
  462.                     *sptr++ = (*lbptr++ >> 8);
  463.                     *sptr++ = (*lbptr++ >> 8);
  464.                     *sptr++ = (*lbptr++ >> 8);
  465.                     *sptr++ = (*lbptr++ >> 8);
  466.                   }
  467.  
  468.                   /* ... then the remaining samples. */
  469.                   for( i = 0UL ; i < cut ; i++ )
  470.                   {
  471.                     *sptr++ = (*lbptr++ >> 8);
  472.                   }
  473.                 }
  474.               }
  475. #endif /* COMMENTED_OUT */
  476.  
  477.               pcm_pos += pcm_count;
  478.             }
  479.  
  480.             /* dispatch error code... */
  481.             switch( pcm_count )
  482.             {
  483.               case MPEGA_ERR_NONE:                                   break;
  484.               case MPEGA_ERR_EOF:                                    break;
  485.               case MPEGA_ERR_BADFRAME: error = DTERROR_INVALID_DATA; break;
  486.               case MPEGA_ERR_MEM:      error = ERROR_NO_FREE_STORE;  break;
  487.               case MPEGA_ERR_NO_SYNC:  error = DTERROR_INVALID_DATA; break;
  488.             }
  489.  
  490.             /* No error ? */
  491.             if( !error )
  492.             {
  493.               ULONG clock  = ((SysBase -> ex_EClockFrequency) * 5UL); /* amiga clock */
  494.               ULONG period = clock / (maid -> maid_MPAS -> dec_frequency);
  495.  
  496.               /* Set up voice header */
  497.               vh -> vh_OneShotHiSamples     = clock / period;
  498.               vh -> vh_RepeatHiSamples      = 0UL;
  499.               vh -> vh_SamplesPerHiCycle    = 0UL;
  500.               vh -> vh_SamplesPerSec        = clock / period;
  501.               vh -> vh_Octaves              = 1UL;
  502.               vh -> vh_Compression          = CMP_NONE;
  503.               vh -> vh_Volume               = 0x10000UL; /* maximum volume */
  504.  
  505.               /* Set misc attributes
  506.                * In fact, SDTA_Period and SDTA_Volume are calculated from SDTA_VoiceHeader info,
  507.                * but we set it here EXPLICITLY that noone can say we didn't pass this to sound.datatype...
  508.                */
  509.               SetDTAttrs( o, NULL, NULL, SDTA_Sample,        sample,
  510.                                          SDTA_SampleLength,  pcm_pos,
  511.                                          SDTA_Period,        period,
  512.                                          TAG_DONE );
  513.  
  514.               sample = NULL; /* The object now owns the sample data */
  515.  
  516.               /* All done... */
  517.               success = TRUE;
  518.             }
  519.  
  520.             MPEGA_close( (maid -> maid_MPAS) );
  521.           }
  522.  
  523.           FreeVec( sample );
  524.         }
  525.         else
  526.         {
  527.           /* no filehandle */
  528.           error = ERROR_NO_FREE_STORE;
  529.         }
  530.       }
  531.     }
  532.     else
  533.     {
  534.       /* can't get required attributes from superclass */
  535.       error = ERROR_OBJECT_WRONG_TYPE;
  536.     }
  537.  
  538.     SetIoErr( error );
  539.  
  540.     return( success );
  541. }
  542.  
  543.  
  544. #ifndef NO_ENCODER
  545. /* The MPEG Audio encoder */
  546. static
  547. ULONG SaveMPEGAudio( struct ClassBase *cb, Object *o, struct dtWrite *dtw )
  548. {
  549.     ULONG retval = 0UL;
  550.     LONG  error  = ERROR_NOT_IMPLEMENTED;
  551.  
  552. #if 0
  553.     /* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
  554.     if( dtw -> dtw_FileHandle )
  555.     {
  556.     }
  557. #endif
  558.  
  559.     /* Store Result2 */
  560.     SetIoErr( error );
  561.  
  562.     return( retval );
  563. }
  564. #endif /* !NO_ENCODER */
  565.  
  566.  
  567. /* mpega.library bitstream hook
  568.  * Required because mpega.library has no way to access an already open filehandle and
  569.  * because we're not allowed to close a filehandle which is owned by "datatypesclass".
  570.  */
  571. static
  572. DISPATCHERFLAGS
  573. ULONG bitstream_hook( REGA0 struct Hook *hook, REGA2 APTR handle, REGA1 MPEGA_ACCESS *access )
  574. {
  575.    struct ClassBase *cb     = (struct ClassBase *)(hook -> h_SubEntry);
  576.    ULONG             retval = 0UL;
  577.  
  578.    switch( access -> func )
  579.    {
  580.       case MPEGA_BSFUNC_OPEN:
  581.       {
  582.          /* We don't know the total size :-( */
  583.          access -> data . open . stream_size = 0L;
  584.  
  585.          /* Return handle */
  586.          retval = (ULONG)(hook -> h_Data);
  587.       }
  588.          break;
  589.  
  590.       case MPEGA_BSFUNC_CLOSE: /* NOP */
  591.          break;
  592.  
  593.       case MPEGA_BSFUNC_READ:
  594.       {
  595.          /* Check valid handle */
  596.          if( handle )
  597.          {
  598.            retval = Read( (BPTR)handle, (access -> data . read . buffer), (access -> data . read . num_bytes) );
  599.          }
  600.       }
  601.          break;
  602.  
  603.       case MPEGA_BSFUNC_SEEK:
  604.       {
  605.          if( handle )
  606.          {
  607.            retval = (Seek( (BPTR)handle, (access -> data . seek . abs_byte_seek_pos), OFFSET_BEGINNING ) == -1L);
  608.          }
  609.       }
  610.          break;
  611.    }
  612.  
  613.    return( retval );
  614. }
  615.  
  616.  
  617.  
  618.  
  619.